package obecto.task.container
{
	import obecto.event.TaskEvent;
	import obecto.event.TaskEventKind;
	import obecto.task.IInterruptibleTask;
	import obecto.task.ITask;
	import obecto.utility.Listener;

	[Event(name="interrupt", type="obecto.event.TaskEvent")]

	public class InterruptibleParallel extends Parallel implements IInterruptibleTask
	{
		protected var _reversing : Boolean = false;
		
		public function InterruptibleParallel()
		{
			super();
		}
		
		override public function addTask(task : ITask) : void
		{
			assertObjectIsNotNull(task);
			assertObjectIsInterruptibleTask(task);
			
			super.addTask(task);
		}
		
		override public function removeTask(task : ITask) : void
		{
			assertObjectIsNotNull(task);
			assertObjectIsInterruptibleTask(task);
			
			super.removeTask(task);
		}
		
		override public function set tasks(value : Array) : void
		{
			for each(var task : Object in value)
			{
				assertObjectIsNotNull(task);
				assertObjectIsInterruptibleTask(task);
			}
			
			super.tasks = value;
		}	
		
		public function end() : void
		{
			_reversing = false;
			
			if(running == true)
			{
				resetTasks();	
				dispatchEvent(new TaskEvent(TaskEvent.INTERRUPT, TaskEventKind.END));
			}
			
			for (var task : Object in _tasks)
			{
				(task as IInterruptibleTask).end();
			}
			
			complete();
		}
		
		public function interrupt() : void
		{
			if(running == true)
			{	
				resetTasks();
				
				_running = false;
				_reversing = false;
				
				for (var task : Object in _tasks)
				{
					(task as IInterruptibleTask).interrupt();
				}
				
				dispatchEvent(new TaskEvent(TaskEvent.INTERRUPT, TaskEventKind.INTERRUPT));
			}
		}
		
		public function reverse() : void
		{
			if(reversing == false)
			{	
				_reversing = true;
				
				if(running == false)
				{
					dispatchEvent(new TaskEvent(TaskEvent.START, TaskEventKind.REVERSE));
					
					if(tasks.length == 0)
					{
						completeReverse();
					}
				}
				else
				{
					_running = true;
					
					dispatchEvent(new TaskEvent(TaskEvent.INTERRUPT, TaskEventKind.REVERSE));
				}
				
				resetTasks();
				
				for (var task : Object in _tasks)
				{
					_tasks[task][LISTENER_KEY] = 
						Listener.createSingleTimeListener(task as ITask, TaskEvent.COMPLETE, handleTaskComplete, true);
						
					(task as IInterruptibleTask).reverse();
				}
			}
		}
		
		public function forward() : void
		{
			if(_running == true && _reversing == true)
			{
				_reversing = false;
				
				dispatchEvent(new TaskEvent(TaskEvent.START, TaskEventKind.FORWARD));
				
				resetTasks();
				
				for (var task : Object in _tasks)
				{
					_tasks[task][LISTENER_KEY] = 
						Listener.createSingleTimeListener(task as ITask, TaskEvent.COMPLETE, handleTaskComplete, true);
					
					var interruptibleTask : IInterruptibleTask = task as IInterruptibleTask;
					
					if(interruptibleTask.running == true)
					{
						interruptibleTask.forward();
					}
					else
					{
						interruptibleTask.start();
					}
				}
			}
		}
		
		public function rollback() : void
		{
			_reversing = true;
			
			if(_running == true)
			{	
				resetTasks();
				dispatchEvent(new TaskEvent(TaskEvent.INTERRUPT, TaskEventKind.ROLLBACK));
			}
			
			for (var task : Object in _tasks)
			{
				(task as IInterruptibleTask).rollback();
			}
				
			completeReverse();
		}
		
		public function get reversing() : Boolean
		{
			return _reversing;
		}
		
		virtual public function captureStartValues(overrideExplicit : Boolean = false) : void
		{
		}
		
		protected function completeReverse() : void
		{
			_running = false;
			_reversing = false;
			
			dispatchEvent(new TaskEvent(TaskEvent.COMPLETE, TaskEventKind.REVERSE))			
		}
		
		override protected function run() : void
		{
			if(_reversing == false)
			{
				super.run();
			}
			else
			{
				reverse();
			}
		}
		
		override protected function finish() : void
		{
			if(_reversing == false)
			{
				super.finish();
			}
			else
			{
				completeReverse();
			}
		}
		
		override protected function runTask(task : ITask) : void
		{
			if(_reversing == false)
			{
				super.runTask(task);
			}
			else
			{
				(task as IInterruptibleTask).reverse();
			}
		}				
		
		protected function assertObjectIsInterruptibleTask(object : Object) : void
		{
			if((object is IInterruptibleTask) == false)
			{
				throw new Error("Object isn't interruptible task.");
			}			
		}
	}
}